package evaluation;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import base.Base;
import base.Tweet;

import classification.Model;
import classification.ModelBase;

/**
 * Classe permettant d'évaluer et de comparer les performances des modèles de
 * Bernoulli et multinomial, suivant les deux types de classification, simple et
 * hiérarchique
 */
public class Evaluation {

	/**
	 * Les statistiques de base
	 */
	private Base base;

	/**
	 * Le chemin du fichier train.txt permettant d'établir les statistiques de
	 * base
	 */
	private String filePath = "fichiers/train.txt";

	/**
	 * Matrice de confusion pour le modèle de Bernoulli, Dimension 4 * 4 :
	 * positive, negative, neutral, irrelevant, (i,j) = nombre de fois où la
	 * classe i a prédit la classe j
	 */
	double[][] matriceBernoulli = new double[4][4];

	/**
	 * Matrice de confusion pour le modèle multinomial, Dimension 4 * 4 :
	 * positive, negative, neutral, irrelevant, (i,j) = nombre de fois où la
	 * classe i a prédit la classe j
	 */
	double[][] matriceMultinomial = new double[4][4];

	/**
	 * Constructeur de l'évaluation
	 */
	public Evaluation() {
		base = new Base(filePath);
		base.construireBase();
	}

	/**
	 * Initialise une matrice de confusion à zéro
	 * 
	 * @param matrice
	 *            la matrice de confusion
	 */
	private void initialisationMatrice(double[][] matrice) {
		for (int i = 0; i < matrice.length; i++)
			for (int j = 0; j < matrice[i].length; j++)
				matrice[i][j] = 0;
	}

	/**
	 * Calcule le coût de la classification pour les deux modèles, Bernoulli et
	 * multinomial
	 * 
	 * @return le coût
	 */
	public String resultCoutQuatreClasse() {

		/* Modèle de Bernoulli */
		double couts = 0;
		String str = "Bernoulli:\n";
		str += " positive\t negative\t neutral\t irrelevant\n";
		double num = 0;
		int sum = 0;
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				sum += matriceBernoulli[i][j];
				str += matriceBernoulli[i][j] + "\t";
				if (i == j)
					num += matriceBernoulli[i][j];
			}
			str += "\n";
		}

		num = num / (sum * 1.0);
		str += "right : " + num + "\n";
		couts = matriceBernoulli[0][1] * 2 + matriceBernoulli[0][2] * 3
				+ matriceBernoulli[0][3] * 3 + matriceBernoulli[1][0] * 2
				+ matriceBernoulli[1][2] * 3 + matriceBernoulli[1][3] * 3
				+ matriceBernoulli[2][0] * 4 + matriceBernoulli[2][1] * 4
				+ matriceBernoulli[2][3] * 1 + matriceBernoulli[3][0] * 4
				+ matriceBernoulli[3][1] * 4 + matriceBernoulli[3][2] * 1;
		str += "Couts: " + couts + "\n";

		/* Modèle multinomial */
		couts = 0;
		num = 0;
		sum = 0;
		str += "Multinomial:\n";
		str += " positive\t negative\t neutral\t irrelevant\n";
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				sum += matriceMultinomial[i][j];
				str += matriceMultinomial[i][j] + "\t";
				if (i == j)
					num += matriceMultinomial[i][j];
			}
			str += "\n";
		}
		num = num / (sum * 1.0);
		str += "right : " + num + "\n";
		couts = matriceMultinomial[0][1] * 2 + matriceMultinomial[0][2] * 3
				+ matriceMultinomial[0][3] * 3 + matriceMultinomial[1][0] * 2
				+ matriceMultinomial[1][2] * 3 + matriceMultinomial[1][3] * 3
				+ matriceMultinomial[2][0] * 4 + matriceMultinomial[2][1] * 4
				+ matriceMultinomial[2][3] * 1 + matriceMultinomial[3][0] * 4
				+ matriceMultinomial[3][1] * 4 + matriceMultinomial[3][2] * 1;
		str += "Couts: " + couts;
		str += "\n\n";
		System.out.println(str);
		return str;
	}

	/**
	 * Renvoie le numéro de la catégorie
	 * 
	 * @param typeClass
	 *            le nom de la catégorie
	 * @return le numéro de la catégorie
	 */
	private int getIndexClassifier(String typeClass) {
		if (typeClass.compareTo("positive") == 0)
			return 0;
		if (typeClass.compareTo("negative") == 0)
			return 1;
		if (typeClass.compareTo("neutral") == 0)
			return 2;
		if (typeClass.compareTo("irrelevant") == 0)
			return 3;

		return -1;
	}

	/**
	 * Fait l'évaluation des deux modèles, Bernoulli et multinomial
	 * 
	 * @param mots_outils
	 *            si on enlève les mots outils ou non
	 * @param mots_frequents
	 *            si on enlève les mots fréquents ou non
	 * @param mots_rares
	 *            si on enlève les mots rares ou non
	 * @param nombre_mots_significatifs
	 *            le nombre de mots les plus significatifs à enlever
	 * @param isHierarchique
	 *            le type de classification
	 * @param lissage
	 *            le paramètre de lissage
	 */
	public void faireEvaluation(boolean mots_outils, boolean mots_frequents,
			boolean mots_rares, int nombre_mots_significatifs,
			boolean isHierarchique, double lissage) {

		/* Initialisation des matrices de confusion */
		initialisationMatrice(matriceBernoulli);
		initialisationMatrice(matriceMultinomial);

		/* Découpage du fichier d'apprentissage en 10 parties égales */
		int nombre = base.getNbTweets() / 10;
		int nombreTotal = nombre * 10;

		/* On parcourt chacune des parties */
		for (int i = 0; i < 10; i++) {

			ModelBase modelBase = new ModelBase(mots_outils, mots_frequents,
					mots_rares, nombre_mots_significatifs, isHierarchique, base);

			Model bernoulli = new Model(isHierarchique, lissage, 0);
			Model multinomial = new Model(isHierarchique, lissage, 1);

			// On construit l'apprentissage
			for (int j = 0; j < nombreTotal; j++) {

				if (j % 10 == i)
					continue;

				Tweet tweet = base.getTweet(j);
				String classType = tweet.getTypeClass();
				String message = tweet.getText();

				/* On supprime les caractères spéciaux */
				// Pattern p = Pattern.compile("[^0-9a-zA-Z]");
				Pattern p = Pattern.compile("[,.!?\":]");
				Matcher m = p.matcher(message);
				message = m.replaceAll(" ");

				modelBase.construireApprentissage(message, classType);
			}
			modelBase.finirBase();

			/* Test pour le modèle de Bernoulli */
			for (int j = 0; j < nombreTotal; j++) {

				if (j % 10 != i)
					continue;

				Tweet tweet = base.getTweet(j);
				String classType = tweet.getTypeClass();
				String message = tweet.getText();

				/* On supprime les caractères spéciaux */
				// Pattern p = Pattern.compile("[^0-9a-zA-Z]");
				Pattern p = Pattern.compile("[,.!?\":]");
				Matcher m = p.matcher(message);
				message = m.replaceAll(" ");

				String preditType = bernoulli.obtenirClasse(message, modelBase);
				int indexI = 0;
				int indexJ = 0;

				indexI = this.getIndexClassifier(preditType);
				indexJ = this.getIndexClassifier(classType);
				matriceBernoulli[indexI][indexJ]++;

			}

			/* Test pour le modèle multinomial */
			for (int j = 0; j < nombreTotal; j++) {

				if (j % 10 != i)
					continue;

				Tweet tweet = base.getTweet(j);
				String classType = tweet.getTypeClass();
				String message = tweet.getText();

				/* On supprime les caractères spéciaux */
				// Pattern p = Pattern.compile("[^0-9a-zA-Z]");
				Pattern p = Pattern.compile("[,.!?\":]");
				Matcher m = p.matcher(message);
				message = m.replaceAll(" ");

				String preditType = multinomial.obtenirClasse(message,
						modelBase);
				int indexI = 0;
				int indexJ = 0;

				indexI = this.getIndexClassifier(preditType);
				indexJ = this.getIndexClassifier(classType);
				matriceMultinomial[indexI][indexJ]++;

			}
		}
	}

	/**
	 * Affiche les paramètres de l'évaluation dans la console
	 * 
	 * @param mots_outils
	 *            si on enlève les mots outils ou non
	 * @param mots_frequents
	 *            si on enlève les mots fréquents ou non
	 * @param mots_rares
	 *            si on enlève les mots rares ou non
	 * @param nombre_mots_significatifs
	 *            le nombre de mots les plus significatifs à enlever
	 * @param isHierarchique
	 *            le type de classification
	 * @param lissage
	 *            le paramètre de lissage
	 * @return la chaîne de caractères obtenue
	 */
	public String write(boolean mots_outils, boolean mots_frequents,
			boolean mots_rares, int nombre_mots_significatifs,
			boolean isHierarchique, double lissage) {

		String str = "isHierarchique: " + isHierarchique + " mots_outils: "
				+ mots_outils + " mots_frequents :" + mots_frequents
				+ " mots_rares:" + mots_rares + " nombre_mots_significatifs:"
				+ nombre_mots_significatifs + " lissage:" + lissage + "\n";

		System.out.println(str);
		return str;
	}

	/**
	 * Main : Fait l'évaluation des deux modèles, Bernoulli et multinomial, et
	 * écrit les résultats dans le fichier Result.txt
	 * 
	 * @param args
	 */
	public static void main(String[] args) {

		Evaluation evaluation = new Evaluation();

		boolean mots_outils = true;
		boolean mots_frequents = true;
		boolean mots_rares = true;
		int nombre_mots_significatifs = 20;
		boolean isHierarchique = true;
		double lissage = 1;

		try {
			File file = new File("Result.txt");
			FileWriter filewriter = new FileWriter(file, true);

			lissage = 0.222;
			mots_outils = false;
			mots_frequents = false;
			mots_rares = true;
			nombre_mots_significatifs = 40;

			String str1 = evaluation.write(mots_outils, mots_frequents,
					mots_rares, nombre_mots_significatifs, isHierarchique,
					lissage);

			evaluation.faireEvaluation(mots_outils, mots_frequents, mots_rares,
					nombre_mots_significatifs, isHierarchique, lissage);

			String str2 = evaluation.resultCoutQuatreClasse();
			filewriter.write(str1);
			filewriter.write(str2);

			filewriter.close();
		} catch (IOException e) {
		} catch (Exception ex) {
		}

	}
}
